﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WikiTrailCreator.Properties;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace WikiTrailCreator.TrailData
{
    class TrailToWikiPageConvertor
    {
        //private static string NEWLINE = Environment.NewLine;
        private static string TWO_NEWLINES = Environment.NewLine + Environment.NewLine;
        // new member variable
        
        /// <summary>
        /// Converts the trail in wiki pages.
        /// </summary>
        /// <param name="trail"></param>
        /// <returns></returns>
        public static void GetWikiPages(Trail trail, out WikiPage mainPage, out WikiPage galleryPage, out PhotoWikiPage[] photoPages)
        {
            mainPage = GetMainTrailPage(trail);
            //galleryPage = GetPhotoGalleryPage(trail);
            photoPages = GetPhotoPages(trail);
        }

        /// <summary>
        /// Generates the code for the main wiki page, for this trail.
        /// </summary>
        private static WikiPage GetMainTrailPage(Trail trail)
        {
            {
                // Generate the code for the main page
                string wikiCode = "";
                wikiCode += SummarySectionToWikiCode(trail);
                wikiCode += LandmarksSectionToWikiCode(trail);
                wikiCode += MapsSectionToWikiCode(trail);
                wikiCode += GettingThereSectionToWikiCode(trail);
                wikiCode += PhotoGallerySectionToWikiCode(trail);
                wikiCode += ConditionsSectionToWikiCode(trail);
                wikiCode += RelatedLinksSectionToWikiCode(trail);

                // Add in the category at the bottom of the page
                wikiCode += "[[Category:" + trail.WikiCategory + "]]" + TWO_NEWLINES;

                string editURL = GetEditUrlForPage(trail.TrailName);
                string viewURL = GetAccessUrlForPage(trail.TrailName);

                return new WikiPage(trail.TrailName, wikiCode, viewURL, editURL);
            }
        }

        /// <summary>
        /// Generates the code for the photo gallery wiki page, if there is a photo gallery.
        /// </summary>
        private static WikiPage GetPhotoGalleryPage(Trail trail)
        {
            if (trail.PhotoGallery.PhotosReferrenced.Length > 0)
            {
                // Generate the code for the photo gallery page
                string wikiCode = "";
                wikiCode += PhotoGallerySectionToWikiCodeNewPage(trail);

                string pageName = "Photos of the " + trail.TrailName;
                string editURL = GetEditUrlForPage(pageName);
                string viewURL = GetAccessUrlForPage(pageName);

                return new WikiPage(pageName, wikiCode, viewURL, editURL);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Generates the code for the photo's pages.
        /// </summary>
        private static PhotoWikiPage[] GetPhotoPages(Trail trail)
        {
            if (trail.AllTrailImages.Length > 0)
            {
                List<PhotoWikiPage> wikiPages = new List<PhotoWikiPage>();

                foreach (LocalImage localImage in trail.AllTrailImages)
                {
                    // Generate the code for the photo page
                    string wikiCode = "";

                    if (localImage.PageText != String.Empty)
                        wikiCode += ConvertStringToWikiCode(localImage.PageText, trail) + TWO_NEWLINES;

                    wikiCode += "This photo was taken along the [[" + trail.TrailName + "]]." + TWO_NEWLINES;

                    string editURL = GetEditUrlForImage(localImage.DisplayText);
                    string viewURL = GetAccessUrlForImage(localImage.DisplayText);
                    string uploadURL = GetUploadUrlForImage(localImage.DisplayText);

                    wikiPages.Add(new PhotoWikiPage(localImage.DisplayText, wikiCode, viewURL, editURL, uploadURL, localImage.LocalPath));
                }

                return wikiPages.ToArray();
            }
            else
            {
                return null;
            }
        }

        private static string SummarySectionToWikiCode(Trail trail)
        {
            SummarySection summarySection = trail.Summary;
            string wikiCode = "==Overview==" + TWO_NEWLINES;

            // Basic trail info:
            wikiCode += "<br />" + TWO_NEWLINES;
            if (summarySection.Length != String.Empty)
                wikiCode += "''Length: " + summarySection.Length + " km''" + TWO_NEWLINES;
            if (summarySection.Difficulty != String.Empty)
                wikiCode += "''Difficulty: " + summarySection.Difficulty + "''" + TWO_NEWLINES;
            if (summarySection.NumHoursToHike != String.Empty)
            {
                wikiCode += "''Effort: ~" + summarySection.NumHoursToHike + " hrs''";

                if (summarySection.NumDaysToHike != String.Empty)
                    wikiCode += " (" + summarySection.NumDaysToHike + " days)" + TWO_NEWLINES;
                else
                    wikiCode += TWO_NEWLINES;
            }

            // Snapashot
            if (summarySection.Snapshot != String.Empty)
                wikiCode += "===Snapshot===" + TWO_NEWLINES + ConvertStringToWikiCode(summarySection.Snapshot, trail) + TWO_NEWLINES;

            // Navigation
            if (summarySection.Navigation != String.Empty)
                wikiCode += "===Navigation===" + TWO_NEWLINES + ConvertStringToWikiCode(summarySection.Navigation, trail) + TWO_NEWLINES;

            // Shelter
            if (summarySection.Shelter != String.Empty)
                wikiCode += "===Shelter===" + TWO_NEWLINES + ConvertStringToWikiCode(summarySection.Shelter, trail) + TWO_NEWLINES;

            // Season
            if (summarySection.Season != String.Empty)
                wikiCode += "===Season===" + TWO_NEWLINES + ConvertStringToWikiCode(summarySection.Season, trail) + TWO_NEWLINES;

            // Dangers and annoyances
            if (summarySection.DangersOrAnnoyances.Length > 0)
            {
                wikiCode += "===Dangers and Annoyances===" + TWO_NEWLINES;

                foreach (Danger danger in summarySection.DangersOrAnnoyances)
                {
                    wikiCode += "====" + danger.Name + "====" + TWO_NEWLINES + ConvertStringToWikiCode(danger.Description, trail) + TWO_NEWLINES;
                }
            }

            // Costs
            if (summarySection.Costs != String.Empty)
                wikiCode += "===Costs===" + TWO_NEWLINES + ConvertStringToWikiCode(summarySection.Costs, trail) + TWO_NEWLINES;

            // Tips
            if (summarySection.Tips.Length > 0)
            {
                wikiCode += "===Tips===" + TWO_NEWLINES;

                foreach (Tip tip in summarySection.Tips)
                {
                    wikiCode += "* " + tip.TipText + NEWLINE;
                }

                wikiCode += NEWLINE;
            }

            return wikiCode;
        }

        private static string LandmarksSectionToWikiCode(Trail trail)
        {
            LandmarksSection landmarksSection = trail.Landmarks;
            string wikiCode = "";

            // Landmarks
            if (landmarksSection.Landmarks.Length > 0)
            {
                wikiCode += "==Important Landmarks==" + TWO_NEWLINES;

                foreach (Landmark landmark in landmarksSection.Landmarks)
                {
                    wikiCode += "===" + landmark.Name + "===" + TWO_NEWLINES + ConvertStringToWikiCode(landmark.Description, trail) + TWO_NEWLINES;
                }
            }

            return wikiCode;
        }

        private static string MapsSectionToWikiCode(Trail trail)
        {
            MapsSection mapsSection = trail.Maps;
            string wikiCode = "";

            // Header?
            if (mapsSection.Purchased != String.Empty 
                || mapsSection.Online != String.Empty
                )
            wikiCode += "==Maps==" + TWO_NEWLINES;

            // Purchased maps
            if (mapsSection.Purchased != String.Empty)
                wikiCode += "===Purchased===" + TWO_NEWLINES + ConvertStringToWikiCode(mapsSection.Purchased, trail) + TWO_NEWLINES;

            // Online maps
            if (mapsSection.Online != String.Empty)
                wikiCode += "===Online===" + TWO_NEWLINES + ConvertStringToWikiCode(mapsSection.Online, trail) + TWO_NEWLINES;

            return wikiCode;
        }

        private static string GettingThereSectionToWikiCode(Trail trail)
        {
            GettingThereSection gettingThereSection = trail.GettingThere;
            string wikiCode = "";

            // Getting there options
            if (gettingThereSection.GettingThereOptions.Length > 0)
            {
                wikiCode += "==Getting There==" + TWO_NEWLINES;

                foreach (GettingThereOption gettingThereOption in gettingThereSection.GettingThereOptions)
                {
                    wikiCode += "===" + gettingThereOption.OptionName + "===" + TWO_NEWLINES + ConvertStringToWikiCode(gettingThereOption.OptionDescription, trail) + TWO_NEWLINES;
                }
            }

            return wikiCode;
        }

        private static string PhotoGallerySectionToWikiCode(Trail trail)
        {
            PhotoGallerySection photoGallerySection = trail.PhotoGallery;
            string wikiCode = "";

            if (photoGallerySection.PhotosReferrenced.Length > 0)
                wikiCode += "==Photos==" + TWO_NEWLINES + "Please see [[Photos of the " + trail.TrailName + "]] for a gallery of images taken along this trail." + TWO_NEWLINES;

            return wikiCode;
        }

        private static string ConditionsSectionToWikiCode(Trail trail)
        {
            ConditionsSection conditionsSection = trail.Conditions;
            string wikiCode = "";

            if (conditionsSection.ConditionsText != String.Empty)
                wikiCode += "==Conditions==" + TWO_NEWLINES + ConvertStringToWikiCode(conditionsSection.ConditionsText, trail) + TWO_NEWLINES;

            return wikiCode;
        }

        private static string RelatedLinksSectionToWikiCode(Trail trail)
        {
            RelatedLinksSection relatedLinksSection = trail.RelatedLinks;
            string wikiCode = "==Related Links==" + TWO_NEWLINES;

            // Related links
            if (relatedLinksSection.RelatedLinks.Length > 0)
            {
                foreach (RelatedLink relatedLink in relatedLinksSection.RelatedLinks)
                {
                    wikiCode += "* [" + relatedLink.URL + " " + relatedLink.Name + "]" + NEWLINE;
                }

                wikiCode += NEWLINE;
            }

            return wikiCode;
        }

        private static string PhotoGallerySectionToWikiCodeNewPage(Trail trail)
        {
            PhotoGallerySection photoGallerySection = trail.PhotoGallery;
            string wikiCode = "";

            // Generate the actual gallery
            if (photoGallerySection.PhotosReferrenced.Length > 0)
            {
                wikiCode += "The following photos were taken along the [[" + trail.TrailName + "]]." + TWO_NEWLINES;

                wikiCode += GenerateGalleryFromLocalImageList(photoGallerySection.PhotosReferrenced);

                wikiCode += NEWLINE;
            }

            return wikiCode;
        }

        private static string GenerateGalleryFromLocalImageList(LocalImage[] localImages)
        {
            string wikiCode = "";

            if (localImages.Length > 0)
            {
                wikiCode += "<gallery perrow=\"3\">" + NEWLINE;

                foreach (LocalImage localImage in localImages)
                {
                    wikiCode += "Image:" + localImage.DisplayText + "|" + localImage.GalleryCaption + NEWLINE;
                }

                wikiCode += "</gallery>";
            }

            return wikiCode;
        }

        private static string GetAccessUrlForPage(string pageName)
        {
            return Settings.Default.WikiURL + "index.php?title=" + pageName;
        }

        private static string GetEditUrlForPage(string pageName)
        {
            return Settings.Default.WikiURL + "index.php?title=" + pageName + "&action=edit";
        }

        private static string GetAccessUrlForImage(string imageName)
        {
            return Settings.Default.WikiURL + "index.php?title=File:" + imageName;
        }

        private static string GetEditUrlForImage(string imageName)
        {
            return Settings.Default.WikiURL + "index.php?title=File:" + imageName + "&action=edit";
        }

        private static string GetUploadUrlForImage(string imageName)
        {
            return Settings.Default.WikiURL + "index.php?title=Special:Upload&wpDestFile=" + imageName;
        }

        private static string ConvertStringToWikiCode(string inputString, Trail trail)
        {
            string outputString = "";

            if (inputString != null && inputString.Length > 0)
            {
                int startIdx = 0;
                bool foundMatch = false;
                do
                {
                    int xmlStartIdx = -1;
                    int xmlEndIdx = -1;
                    string strTagName = String.Empty;
                    foundMatch = false;

                    // Check for tags...
                    // <gallery>
                    int xmlGalleryStartIdx = inputString.IndexOf("<imagegallery", startIdx);
                    int xmlGalleryEndIdx = inputString.IndexOf("</imagegallery>", startIdx) + "</imagegallery>".Length;
                    if (xmlGalleryStartIdx == -1 || xmlGalleryEndIdx == -1)
                    {
                        xmlGalleryStartIdx = xmlGalleryEndIdx = -1;
                    }
                    else if (xmlStartIdx == -1 || xmlGalleryStartIdx < xmlStartIdx)
                    {
                        xmlStartIdx = xmlGalleryStartIdx;
                        xmlEndIdx = xmlGalleryEndIdx;
                        strTagName = "imagegallery";
                    }
                    
                    // <thumbnail>
                    int xmlThumbnailStartIdx = inputString.IndexOf("<thumbnail", startIdx);
                    int xmlThumbnailEndIdx = inputString.IndexOf("</thumbnail>", startIdx) + "</thumbnail>".Length;
                    if (xmlThumbnailStartIdx == -1 || xmlThumbnailEndIdx == -1)
                    {
                        xmlThumbnailStartIdx = xmlThumbnailEndIdx = -1;
                    }
                    else if (xmlStartIdx == -1 || xmlThumbnailStartIdx < xmlStartIdx)
                    {
                        xmlStartIdx = xmlThumbnailStartIdx;
                        xmlEndIdx = xmlThumbnailEndIdx;
                        strTagName = "thumbnail";
                    }

                    // If no tag is found, then just add the rest of the string...
                    if (strTagName == String.Empty)
                    {
                        outputString += inputString.Substring(startIdx);
                    }
                    // Otherwise...
                    else
                    {
                        // Add up to the selected tag...
                        int nonXmlTextLength = xmlStartIdx - startIdx;
                        outputString += inputString.Substring(startIdx, nonXmlTextLength);
                    
                        // Convert tag to wiki code...
                        int length = xmlEndIdx - xmlStartIdx;
                        string xmlText = "";
                        if (length > 0)
                        {
                            xmlText = inputString.Substring(xmlStartIdx, length);

                            string wikiCode = xmlText;
                            switch (strTagName)
                            {
                                case "imagegallery":
                                    Imagegallery gallery = Imagegallery.Deserialize(xmlText);
                                    wikiCode = GenerateWikiGalleryText(gallery, trail);
                                    break;

                                case "thumbnail":
                                    Thumbnail thumbnail = Thumbnail.Deserialize(xmlText);
                                    wikiCode = GenerateWikiThumbnailText(thumbnail, trail);
                                    break;

                                default:
                                    break;
                            }

                            outputString += wikiCode;

                            foundMatch = true;
                        }
                    }

                    startIdx = xmlEndIdx;
                } while (foundMatch == true && startIdx < inputString.Length);
            }

            return outputString;
        }

        private static string GenerateWikiThumbnailText(Thumbnail thumbnail, Trail trail)
        {
            string wikiCode = "";

            if (thumbnail != null)
            {
                List<LocalImage> localImages = new List<LocalImage>(trail.AllTrailImages);

                foreach (LocalImage localImage in localImages)
                {
                    if (localImage.InternalId == thumbnail.InternalImageId)
                    {
                        if (localImage.GalleryCaption != String.Empty)
                        {
                            wikiCode += "[[Image:" + localImage.DisplayText + "|thumb|" + localImage.GalleryCaption + "]]" + Environment.NewLine;
                        }
                        else
                        {
                            wikiCode += "[[Image:" + localImage.DisplayText + "|thumb]]" + Environment.NewLine;
                        }
                        break;
                    }
                }
            }

            return wikiCode;
        }

        private static string GenerateWikiGalleryText(Imagegallery gallery, Trail trail)
        {
            string xml = "";

            if (gallery != null && gallery.Images.Length > 0)
            {
                xml += "<gallery perrow=\"3\">" + Environment.NewLine;

                List<LocalImage> localImages = new List<LocalImage>(trail.AllTrailImages);

                foreach (Guid internalImageId in gallery.Images)
                {
                    foreach (LocalImage localImage in localImages)
                    {
                        if (localImage.InternalId == internalImageId)
                        {
                            if (localImage.GalleryCaption != String.Empty)
                            {
                                xml += "Image:" + localImage.DisplayText + "|" + localImage.GalleryCaption + Environment.NewLine;
                            }
                            else
                            {
                                xml += "Image:" + localImage.DisplayText + Environment.NewLine;
                            }
                            break;
                        }
                    }
                }

                xml += "</gallery>";
            }

            return xml;
        }
    }

    /// <summary>
    /// XML class for deserializing internal gallery tags in user text.
    /// </summary>
    [XmlRoot("imagegallery")]
    public class Imagegallery
    {
        List<Guid> images = new List<Guid>();

        [XmlArray("images")]
        [XmlArrayItem("image")]
        public Guid[] Images
        {
            get 
            { 
                return images.ToArray(); 
            }
            set 
            {
                images.Clear();
                images.AddRange(value);
            }
        }

        public static string Serialize(Imagegallery gallery)
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Imagegallery));
            xmlSerializer.Serialize(memoryStream, gallery);
            StreamReader streamReader = new StreamReader(memoryStream);
            string xmlText = streamReader.ReadToEnd();
            streamReader.Close();
            memoryStream.Close();

            return xmlText;
        }

        public static Imagegallery Deserialize(string xmlText)
        {
            Imagegallery gallery = null;

            MemoryStream memoryStream = new MemoryStream();
            StreamWriter streamWriter = new StreamWriter(memoryStream);
            streamWriter.Write(xmlText);
            streamWriter.Flush();
            memoryStream.Position = 0;
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Imagegallery));
            gallery = (Imagegallery)xmlSerializer.Deserialize(memoryStream);
            streamWriter.Close();
            memoryStream.Close();

            return gallery;
        }
    }

    /// <summary>
    /// XML class for deserializing internal thumbnail tags in user text.
    /// </summary>
    [XmlRoot("thumbnail")]
    public class Thumbnail
    {
        [XmlAttribute("id")]
        public Guid InternalImageId;

        public static string Serialize(Thumbnail thumbnail)
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Thumbnail));
            xmlSerializer.Serialize(memoryStream, thumbnail);
            memoryStream.Position = 0;
            StreamReader streamReader = new StreamReader(memoryStream);
            string xmlText = streamReader.ReadToEnd();
            streamReader.Close();
            memoryStream.Close();

            return xmlText;
        }

        public static Thumbnail Deserialize(string xmlText)
        {
            Thumbnail thumbnail = null;

            MemoryStream memoryStream = new MemoryStream();
            StreamWriter streamWriter = new StreamWriter(memoryStream);
            streamWriter.Write(xmlText);
            streamWriter.Flush();
            memoryStream.Position = 0;
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Thumbnail));
            thumbnail = (Thumbnail)xmlSerializer.Deserialize(memoryStream);
            streamWriter.Close();
            memoryStream.Close();

            return thumbnail;
        }
    }
}
